<html xmlns="https://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="zh-CN" />
<link href="../style/css/manual-zip.css" rel="stylesheet" media="all" type="text/css" title="Main stylesheet" />
<link href="../style/css/manual-zip-100pc.css" rel="alternate stylesheet" media="all" type="text/css" title="No Sidebar - Default font size" />
<link href="../style/css/manual-print.css" rel="stylesheet" media="print" type="text/css" />
<title>URL重写技术细节 － Apache 2.2 中文手册 [金步国]</title>
<script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?d286c55b63a3c54a1e43d10d4c203e75"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script>
</head>
<body id="manual-page"><div id="page-header">
<p class="menu"><a href="../mod/index.html">模块索引</a> | <a href="../mod/directives.html">指令索引</a> | <a href="../faq/index.html">常见问题</a> | <a href="../glossary.html">词汇表</a> | <a href="../sitemap.html">站点导航</a></p><p class="apache">Apache HTTP Server 版本2.2</p><img alt="" src="../images/feather.gif" /></div>
<div class="up"><a href="index.html"><img title="&lt;-" alt="&lt;-" src="../images/left.gif" /></a></div>
<div id="path"><a href="https://www.apache.org/">Apache</a> &gt; <a href="https://httpd.apache.org/">HTTP Server</a> &gt; <a href="https://httpd.apache.org/docs/">文档</a> &gt; <a href="../index.html">版本2.2</a> &gt; <a href="index.html">URL重写</a></div>

<div id="translation-info">　　 <a href="../translator_announcement.html#thanks">致谢</a> | 本篇译者：<a href="../../../index.html">金步国</a>(<a href="../../../index.html">作品集</a>) | 本页最后更新：2006年10月20日</div>
<div id="page-content"><div id="preamble"><h1>URL重写技术细节</h1>


<p>本文探讨一些<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>和URL匹配的技术细节。</p>
</div>
	<div class="top"><a href="rewrite_tech.html#page-header"><img alt="top" src="../images/up.gif" /></a></div>
<div class="section">
<h2><a name="Internal" id="Internal">内部处理</a></h2>

      <p>此模块的内部处理极为复杂，但是为了使一般用户避免犯低级错误，也让管理员能充分利用其功能，在此仍然做一下说明。</p>
</div><div class="top"><a href="rewrite_tech.html#page-header"><img alt="top" src="../images/up.gif" /></a></div>
<div class="section">
<h2><a name="InternalAPI" id="InternalAPI">API阶段</a></h2>

      <p>首先，你必须了解Apache是分若干阶段来处理HTTP请求的。Apache API对每个阶段都提供了一个hook程序。<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>使用两个hook程序：其一，从URL到文件名的转换hook(用在读取HTTP请求之后、授权开始之前)； 其二，修正hook(用在授权阶段和读取目录级配置(<code>.htaccess</code>)之后、内容处理器激活之前)。</p>

      <p>所以，Apache收到一个请求并且确定了响应主机(或虚拟主机)之后，重写引擎即开始处理服务器级配置中的所有<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>指令(此时处于从URL到文件名转换的阶段)，此阶段完成后，最终的数据目录便确定了。接下来进入修正程序段并触发目录级配置中的<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>指令。这两个阶段并不是泾渭分明的，但都实施了把URL重写成新的URL或者文件名。虽然API最初不是为此目的而设计的，但是现在它已经成为了API的一种用途。记住以下两点，会有助于更好地理解：</p>

      <ol>
        <li>虽然<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>可以将URL重写为新的URL或文件名，甚至将文件名重写为新的文件名，但是之前的API只提供从URL到文件名的hook。在Apache 2.0中，增加了两个丢失的hook以使得处理过程更加清晰。不过这样做并没有给用户带来麻烦，用户只需记住这样一个事实：借助从URL到文件名的hook比最初API设计的目标功能更强大。</li>

        <li>令人难以置信的是，<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>还提供了目录级的URL操作(<code>.htaccess</code>文件)，而这些文件必须在将URL转换成文件名之后才会被处理(这是必须的，因为<code>.htaccess</code>存在于文件系统中)。换句话说，根据API阶段，这时再处理任何URL操作已经太晚了。为了解决这个"鸡和蛋"的问题，<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>使用了一个小技巧：在进行一个目录级的URL/文件名操作时，先把文件名重写回相应的URL(通常这个操作是不可行的，但是参考下面的<code>RewriteBase</code>指令就能明白它是怎么实现的了)，然后，对这个新的URL建立一个新的内部的子请求，再重新开始API阶段的执行。

          <p>另外，<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>尽力使这些复杂的操作对用户透明。但仍须记住：服务器级的URL操作速度快而且效率高，而目录级的操作由于这个"鸡和蛋"的问题速度较慢而且效率也低。但从另一个侧面看，这却是<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>得以为一般用户提供(局部限制的)URL操作的唯一方法。</p>
        </li>
      </ol>

      <p>牢记这两点！</p>
</div><div class="top"><a href="rewrite_tech.html#page-header"><img alt="top" src="../images/up.gif" /></a></div>
<div class="section">
<h2><a name="InternalRuleset" id="InternalRuleset">规则集的处理</a></h2>

      <p>当<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>在这两个API阶段中开始执行时，它会读取配置结构中配置好的 (或者是在服务启动时建立的服务器级的，或者是在遍历目录采集到的目录级的)规则集，然后，启动URL重写引擎来处理(带有一个或多个条件的)规则集。无论是服务器级的还是目录级的规则集，都是由同一个URL重写引擎处理，只是最终结果处理不同而已。</p>

      <p>规则集中规则的顺序是很重要的，因为重写引擎是按一种特殊的顺序处理的：逐个遍历每个规则(<code class="directive"><a href="../mod/mod_rewrite.html#rewriterule">RewriteRule</a></code>指令)，如果出现一个匹配条件的规则，则可能回头遍历已有的规则条件(<code>RewriteCond</code>指令)。由于历史的原因，条件规则是前置的，所以控制流程略显冗长，细节见图-1。</p>
<p class="figure">
      <img src="../images/mod_rewrite_fig1.gif" width="428" height="385" alt="[Needs graphics capability to display]" /><br />
      <dfn>图-1:</dfn>重写规则集中的控制流
</p>
      <p>可见，URL首先与每个规则的<em>Pattern</em>匹配，如果匹配失败，<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>将立即终止此规则的处理，继而处理下一个规则。如果匹配成功，<code class="module"><a href="../mod/mod_rewrite.html">mod_rewrite</a></code>将寻找相应的规则条件，如果一个条件都没有，则简单地用<em>Substitution</em>构造的新值来替换URL，然后继续处理其他规则；但是如果条件存在，则开始一个内部循环按其列出的顺序逐个处理。对规则条件的处理有所不同：URL并不与模式进行匹配，而是首先通过扩展变量、反向引用、查找映射表等步骤建立一个<em>TestString</em>字符串，然后用它来与<em>CondPattern</em>匹配。如果匹配失败，则整个条件集和对应的规则失败；如果匹配成功，则执行下一个规则直到所有条件执行完毕。如果所有条件得以匹配，则以<em>Substitution</em>替换URL，并且继续处理。</p>

</div></div>
<div id="footer">
<p class="apache">本文允许自由的转载、引用、再分发，但必须保留译者署名并注明出处；详见：<a href="../translator_announcement.html#announcement">版权声明</a>。</p>
<p class="menu"><a href="../mod/index.html">模块索引</a> | <a href="../mod/directives.html">指令索引</a> | <a href="../faq/index.html">常见问题</a> | <a href="../glossary.html">词汇表</a> | <a href="../sitemap.html">站点导航</a></p></div>
</body></html>
